/*
 * Copyright 2022 Huawei Cloud Computing Technology Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.huawei.cloudapp.ui.activity;

import static android.os.SystemClock.sleep;
import static com.huawei.cloudapp.utils.CasCommonUtils.isSupportH265;
import static com.huawei.cloudapp.utils.CasConstantsUtil.AES_KEY;
import static com.huawei.cloudapp.utils.CasConstantsUtil.AUTH_TS;
import static com.huawei.cloudapp.utils.CasConstantsUtil.AVAILABLE_PLAYTIME;
import static com.huawei.cloudapp.utils.CasConstantsUtil.BACKGROUND_TIMEOUT;
import static com.huawei.cloudapp.utils.CasConstantsUtil.DEFINITION_BEST;
import static com.huawei.cloudapp.utils.CasConstantsUtil.DIRECT;
import static com.huawei.cloudapp.utils.CasConstantsUtil.FRAME_TYPE;
import static com.huawei.cloudapp.utils.CasConstantsUtil.FRAME_TYPE_H265;
import static com.huawei.cloudapp.utils.CasConstantsUtil.IAM_USERNAME;
import static com.huawei.cloudapp.utils.CasConstantsUtil.IP;
import static com.huawei.cloudapp.utils.CasConstantsUtil.MANAGEMENT;
import static com.huawei.cloudapp.utils.CasConstantsUtil.PHONE_ID;
import static com.huawei.cloudapp.utils.CasConstantsUtil.PHYSICAL_HEIGHT;
import static com.huawei.cloudapp.utils.CasConstantsUtil.PHYSICAL_WIDTH;
import static com.huawei.cloudapp.utils.CasConstantsUtil.PORT;
import static com.huawei.cloudapp.utils.CasConstantsUtil.PROJECT_ID;
import static com.huawei.cloudapp.utils.CasConstantsUtil.QUALITY;
import static com.huawei.cloudapp.utils.CasConstantsUtil.REGION_ID;
import static com.huawei.cloudapp.utils.CasConstantsUtil.RESOLUTION_1080P;
import static com.huawei.cloudapp.utils.CasConstantsUtil.SESSION_ID;
import static com.huawei.cloudapp.utils.CasConstantsUtil.TICKET;
import static com.huawei.cloudapp.utils.CasConstantsUtil.TOKEN;
import static com.huawei.cloudapp.utils.CasConstantsUtil.TOKEN_EXPIRE_TIME;
import static com.huawei.cloudapp.utils.CasConstantsUtil.TOUCH_TIMEOUT;
import static com.huawei.cloudapp.utils.CasConstantsUtil.USERNAME;
import static com.huawei.cloudapp.utils.CasConstantsUtil.USER_ID;
import static com.huawei.cloudapp.utils.CasConstantsUtil.USER_INFO;
import static com.huawei.cloudapp.utils.CasConstantsUtil.WIDTH;
import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_CAMERA;
import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_LOCATION;
import static com.huawei.cloudphone.api.CloudPhoneParas.DEV_TYPE_MICROPHONE;
import static com.huawei.cloudphone.api.CloudPhoneParas.DevType.DEV_PHONE;
import static com.huawei.cloudphone.utils.CasConstantsUtil.CLIENT_MODE;

import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.CellSignalStrengthGsm;
import android.telephony.CellSignalStrengthLte;
import android.telephony.CellSignalStrengthNr;
import android.telephony.CellSignalStrengthTdscdma;
import android.telephony.CellSignalStrengthWcdma;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentActivity;

import com.google.gson.Gson;
import com.hjq.toast.ToastParams;
import com.hjq.toast.Toaster;
import com.hjq.toast.style.CustomToastStyle;
import com.huawei.cloudapp.BuildConfig;
import com.huawei.cloudapp.R;
import com.huawei.cloudapp.common.CASLog;
import com.huawei.cloudapp.common.CasCommonDialog;
import com.huawei.cloudapp.common.CasListener;
import com.huawei.cloudapp.common.CasRecord;
import com.huawei.cloudapp.common.CasState;
import com.huawei.cloudapp.model.IConnectInfoModel;
import com.huawei.cloudapp.model.IHandleData;
import com.huawei.cloudapp.model.IUserModel;
import com.huawei.cloudapp.model.bean.CustomException;
import com.huawei.cloudapp.model.bean.User;
import com.huawei.cloudapp.model.bean.management.CasConnectInfo;
import com.huawei.cloudapp.presenter.ConnectInfoPresenter;
import com.huawei.cloudapp.presenter.UserPresenter;
import com.huawei.cloudapp.utils.CasAESKeystoreUtils;
import com.huawei.cloudapp.utils.CasAdaptPhoneUtils;
import com.huawei.cloudapp.utils.CasCommonUtils;
import com.huawei.cloudphone.api.CloudAppDataListener;
import com.huawei.cloudphone.api.CloudPhoneClipboardListener;
import com.huawei.cloudphone.api.CloudPhoneManager;
import com.huawei.cloudphone.api.CloudPhoneOrientationChangeListener;
import com.huawei.cloudphone.api.CloudPhoneParas;
import com.huawei.cloudphone.api.CloudPhonePermissionInfo;
import com.huawei.cloudphone.api.CloudPhonePermissionRequestListener;
import com.huawei.cloudphone.api.CloudPhoneStateListener;
import com.huawei.cloudphone.api.ICloudPhone;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class CasCloudPhoneActivity extends FragmentActivity implements IHandleData<Object> {
    private static final String TAG = "CasCloudPhoneActivity";

    // Intent Trans Key
    public static final String ORIENTATION = "orientation";
    public static final String MEDIA_CONFIG = "media_config";

    private static final int WAITING_HOME_LONG_PRESS_TIME = 1000;
    private static final int WAITING_APP_SWITCH_LONG_PRESS_TIME = 300;

    private static final int CHANGE_ROTA = 100;
    private static final int MSG_CONNECT_FAILED = 102;
    private static final int MSG_STATE_CHANGE = 103;
    private static final int MSG_SHOW_TRAIL_PLAY_TIMEOUT = 104;
    private static final int MSG_RECONNECT_FAILED = 105;
    private static final int MSG_GET_START_PARAM_RESPONSE = 106;
    private static final int MSG_CANCLE_EXIT = 107;
    private static final int MSG_PRESS_HOME = 108;
    private static final int MSG_PRESS_APP_SWITCH = 109;
    private static final int MSG_PERMISSION_REQUEST = 110;
    private static final int STATE_DEINIT = 1;
    private static CasListener mCasListener = null;

    // flag
    private HashMap<String, String> mMediaConfig = null;
    private ProgressBar mProgressBar = null;
    private int currentRotation = -1;
    private CasCommonDialog quitDialog;
    private CasCommonDialog mDialog;
    private CasRecord mCasRecord;
    private CasRecord mCasUserRecord;

    // intent trans value
    private CasConnectInfo mConnectInfo;
    private User mUserInfo;
    private String mPhoneId;
    private String mSelectedRegion;
    private boolean bIsStart = false;
    private int mOrientation = 0;
    private String mFrameType = FRAME_TYPE_H265;
    private String mResolution = RESOLUTION_1080P;
    private FrameLayout mFrameLayout = null;
    private boolean mIsTrailPlayTimeout = false;
    private boolean mIsActivityBackground = false;
    private boolean mIsStopCloudPhoneCalled = false;
    private ICloudPhone mCloudPhone;
    private TextView mNavigationTextView;
    private TextView mStatisticsTextView;

    private TextView mMiniStatisticsTextView;
    private CheckBox mStatisticsCheckBox;

    private TextView mSensorInfoTextView;

    private CheckBox mSensorInfoCheckBox;
    private Timer mTimer = new Timer();
    private boolean isReadyToExit;
    private Toast mToast;

    private int storagePermission = 1;

    private Handler handler = new Handler() {
        @SuppressLint("RestrictedApi")
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case CHANGE_ROTA:
                    CASLog.d(TAG, "--->CHANGE_ROTA:%d", msg.arg1);
                    setRotation(msg.arg1);
                    break;
                case MSG_CONNECT_FAILED:
                    showDialog(getResources().getString(R.string.cas_phone_connect_server_fail_tip_message));
                    break;
                case MSG_SHOW_TRAIL_PLAY_TIMEOUT:
                    CASLog.i(TAG, "showPlayTimeoutDialog...");
                    showDialog(getResources().getString(R.string.cas_phone_play_timeout_tip_message));
                    break;
                case MSG_STATE_CHANGE:
                    processStateChangeMsg(msg.arg1, (String) msg.obj);
                    break;
                case MSG_RECONNECT_FAILED:
                    try {
                        mCloudPhone.exitCloudPhone();
                    } catch (Exception e) {
                        CASLog.e(TAG, "exitCloudPhone failed");
                    }
                    showDialog(getResources().getString(R.string.cas_phone_reconnect_server_fail_tip_message));
                    break;
                case MSG_CANCLE_EXIT:
                    isReadyToExit = false;
                    mToast.cancel();
                    break;
                case MSG_PRESS_HOME:
                    KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_HOME);
                    dispatchKeyEvent(event);
                    event = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_HOME);
                    dispatchKeyEvent(event);
                    break;
                case MSG_PRESS_APP_SWITCH:
                    event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_APP_SWITCH);
                    dispatchKeyEvent(event);
                    event = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_APP_SWITCH);
                    dispatchKeyEvent(event);
                    break;
                case MSG_PERMISSION_REQUEST:
                    handlePermissionMsg((CloudPhonePermissionInfo) msg.obj);
                    break;
                default:
                    break;
            }
        }
    };

    /**
     * set screen long light
     */
    public static void keepScreenLongLight(Activity activity, boolean isOpenLight) {
        CASLog.d(TAG, "@keepScreenLongLight isOpenLight=" + isOpenLight);
        Window window = activity.getWindow();
        if (isOpenLight) {
            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        } else {
            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
    }

    /**
     * Activity:onCreate
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setFullScreen();
        super.onCreate(savedInstanceState);
        setContentView(R.layout.cas_activity_fullscreen);
        keepScreenLongLight(this, true);
        getIntentData();
        mToast = Toast.makeText(this, null, Toast.LENGTH_SHORT);
        mCasRecord = new CasRecord(getSharedPreferences("input_history", MODE_PRIVATE));
        // get views
        mProgressBar = findViewById(R.id.loading_progress_bar);
        mFrameLayout = (FrameLayout) findViewById(R.id.frame_layout);

        mMiniStatisticsTextView = findViewById(R.id.cas_mini_statistic_view);
        mMiniStatisticsTextView.setTextColor(Color.argb(255, 216, 234, 196));

        mStatisticsTextView = findViewById(R.id.cas_statistic_view);
        mStatisticsTextView.setTextColor(Color.GREEN);

        mStatisticsCheckBox = findViewById(R.id.cas_stats_checkBox);
        mStatisticsCheckBox.setTextColor(Color.GREEN);
        mStatisticsCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                mStatisticsTextView.setVisibility(View.VISIBLE);
                float density = getResources().getDisplayMetrics().density;
                mStatisticsTextView.setHeight((int) (50 * density));
            } else {
                mStatisticsTextView.setVisibility(View.INVISIBLE);
            }
        });
        mTimer.schedule(new TimerTask() {
            @Override
            public void run() {
                runOnUiThread(() -> {
                    onUpdateSensorInfo();
                    onUpdateStatisticInfo();
                });
            }
        }, 1000, 1000);

        mSensorInfoTextView = findViewById(R.id.cas_sensor_view);
        mSensorInfoTextView.setTextColor(Color.GREEN);

        mSensorInfoCheckBox = findViewById(R.id.cas_sensor_checkBox);
        mSensorInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
            if (isChecked) {
                mSensorInfoTextView.setVisibility(View.VISIBLE);
                float density = getResources().getDisplayMetrics().density;
                mSensorInfoTextView.setHeight((int) (50 * density));
            } else {
                mSensorInfoTextView.setVisibility(View.INVISIBLE);
            }
        });
        if (!BuildConfig.IS_DISPLAY_STATISTICS) {
            mMiniStatisticsTextView.setVisibility(View.INVISIBLE);
            mStatisticsCheckBox.setVisibility(View.INVISIBLE);
            mSensorInfoCheckBox.setVisibility(View.INVISIBLE);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{
                    Manifest.permission.READ_EXTERNAL_STORAGE,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
            }, storagePermission);
        }

        mNavigationTextView = findViewById(R.id.cas_navigaiton);
        mNavigationTextView.setOnTouchListener(new View.OnTouchListener() {
            @SuppressLint({"RestrictedApi", "ClickableViewAccessibility"})
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                switch (motionEvent.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        handler.sendEmptyMessageDelayed(MSG_PRESS_HOME, WAITING_HOME_LONG_PRESS_TIME);
                        handler.sendEmptyMessageDelayed(MSG_PRESS_APP_SWITCH, WAITING_APP_SWITCH_LONG_PRESS_TIME);
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        handler.removeMessages(MSG_PRESS_HOME);
                        handler.removeMessages(MSG_PRESS_APP_SWITCH);
                        break;
                    case MotionEvent.ACTION_MOVE:
                        if (!isInView(motionEvent, view)) {
                            handler.removeMessages(MSG_PRESS_HOME);
                            handler.removeMessages(MSG_PRESS_APP_SWITCH);
                        }
                        break;
                }
                motionEvent.setLocation(motionEvent.getX() + (mFrameLayout.getWidth() / 3),
                        mFrameLayout.getHeight() - (view.getHeight() - motionEvent.getY()));
                mFrameLayout.dispatchTouchEvent(motionEvent);
                return true;
            }
        });

        setRotation(mOrientation);
        try {
            mCloudPhone = CloudPhoneManager.createCloudPhoneInstance();
            mCloudPhone.setDisplayMode(CloudPhoneParas.DisplayMode.DISPLAY_MODE_FILL);
            mCloudPhone.registerPermissionRequestListener(new CloudPhonePermissionRequestListenerImpl());
            mCloudPhone.init(this, DEV_PHONE);

            mCloudPhone.registerCloudPhoneStateListener(new CloudPhoneStateListenerImpl());
            mCloudPhone.registerOnOrientationChangeListener(new CloudPhoneOrientationListener());
            mCloudPhone.registerCloudAppDataListener(new CloudAppDataListenerImpl());
            mCloudPhone.registerClipboardListener(new CloudPhoneClipboardListenerImpl());

            if (mMediaConfig == null) {
                mMediaConfig = new HashMap<String, String>();
            }
            if (isSupportH265()) {
                mMediaConfig.put(FRAME_TYPE, "h265");
            } else {
                mMediaConfig.put(FRAME_TYPE, "h264");
            }

            mMediaConfig.put(QUALITY, DEFINITION_BEST);
            DisplayMetrics metrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
            mMediaConfig.put(PHYSICAL_WIDTH, String.valueOf(metrics.widthPixels));
            mMediaConfig.put(PHYSICAL_HEIGHT, String.valueOf(metrics.heightPixels));
            mCloudPhone.setMediaConfig(mMediaConfig);

            getPhoneConnectInfo(mPhoneId);
        } catch (IllegalArgumentException e) {
            showDialog(getResources().getString(R.string.cas_phone_input_param_invalid));
        } catch (Exception e) {
            CASLog.e(TAG, "startCloudApp start failed." + e.getMessage());
            showDialog(getResources().getString(R.string.cas_phone_start_fail_tip_message));
        }
    }

    public void onUpdateStatisticInfo() {
        LocalDateTime dateTime = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
        String timeStr = dateTime.format(formatter);
        String simpleStr = timeStr + " ";
        simpleStr += mCloudPhone.getSimpleStatisticInfo();
        mMiniStatisticsTextView.setText(simpleStr);

        if (mStatisticsTextView.getVisibility() != View.VISIBLE) {
            return;
        }

        String str = timeStr + "\n";
        str += "网络数据>>\n";
        str += mCloudPhone.getVideoStatisticInfo();
        str += "\n";
        str += getTowerInfo();
        mStatisticsTextView.setText(str);
    }

    public void onUpdateSensorInfo() {
        if (mSensorInfoTextView.getVisibility() != View.VISIBLE) {
            return;
        }

        String str = "传感器启停状况>>";
        str += mCloudPhone.getSensorStatusInfo();
        mSensorInfoTextView.setText(str);
    }

    /**
     * Activity:onStart
     */
    @Override
    public void onStart() {
        CASLog.i(TAG, "onStart");
        super.onStart();
    }

    /**
     * Activity:onRestart
     */
    @Override
    public void onRestart() {
        CASLog.i(TAG, "onRestart");
        super.onRestart();
    }

    /**
     * Activity:onStop
     */
    @Override
    public void onStop() {
        CASLog.i(TAG, "onStop");
        super.onStop();
    }

    /**
     * Activity:onPause
     */
    @Override
    public void onPause() {
        CASLog.i(TAG, "onPause");
        super.onPause();
        mIsActivityBackground = true;
    }

    /**
     * Activity:onResume
     */
    @Override
    public void onResume() {
        CASLog.i(TAG, "onResume");
        if (mIsActivityBackground) {
            setFullScreen();
        }
        mIsActivityBackground = false;
        super.onResume();
        if (mIsTrailPlayTimeout) {
            CASLog.i(TAG, "trail play timeout");
            Message message = new Message();
            message.what = MSG_SHOW_TRAIL_PLAY_TIMEOUT;
            handler.sendMessage(message);
        }

        sendClipboardData();
    }

    /**
     * Activity:onDestroy
     */
    @Override
    protected void onDestroy() {
        CASLog.i(TAG, "onDestroy");
        keepScreenLongLight(this, false);
        super.onDestroy();
        stopCloudPhone();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (mCloudPhone == null) {
            return;
        }
        if (requestCode == DEV_TYPE_CAMERA || requestCode == DEV_TYPE_MICROPHONE || requestCode == DEV_TYPE_LOCATION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                mCloudPhone.sendPermissionResult(requestCode, PackageManager.PERMISSION_GRANTED);
                return;
            }
        }
        if (requestCode == storagePermission) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                CASLog.i(TAG, "storagePermission");
            }
        }
    }

    /**
     * get data with intent
     */
    private void getIntentData() {
        CASLog.i(TAG, "getIntentData");
        Intent intent = getIntent();
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            mUserInfo = intent.getParcelableExtra(User.BUNDLE_KEY);
            mPhoneId = bundle.getString(PHONE_ID);
            mSelectedRegion = bundle.getString(REGION_ID);
            mOrientation = bundle.getInt(ORIENTATION);
            mMediaConfig = (HashMap<String, String>) bundle.getSerializable(MEDIA_CONFIG);
            if (mUserInfo == null || mPhoneId == null || mPhoneId.isEmpty()
                    || mSelectedRegion == null || mSelectedRegion.isEmpty()) {
                showDialog(getResources().getString(R.string.cas_phone_get_auth_param_fail_tip_message));
                Log.e(TAG, "The data from intent is empty. ");
            }
        }
    }

    private void processStateChangeMsg(int code, String msg) {
        CASLog.i(TAG, "processStateChangeMsg code = " + code);
        switch (code) {
            case CasState.CAS_START_SUCCESS:
                mProgressBar.setVisibility(View.GONE);
                setStartSuccessFlag();
                handleStartSuccessMsg(msg);
                break;
            case CasState.CAS_SERVER_UNREACHABLE:
                showDialog(getResources().getString(R.string.cas_phone_connect_server_fail_tip_message));
                break;
            case CasState.CAS_ENGINE_START_FAILED:
            case CasState.CAS_PARAMETER_MISSING:
                showDialog(getResources().getString(R.string.cas_phone_start_fail_tip_message));
                break;
            case CasState.CAS_RESOURCE_IN_USING:
                showDialog(getResources().getString(R.string.cas_phone_resource_inusing_tip_message));
                break;
            case CasState.CAS_INVALID_CMD:
                try {
                    mCloudPhone.exitCloudPhone();
                } catch (Exception e) {
                    CASLog.e(TAG, "exitCloudPhone failed");
                }
                showDialog(getResources().getString(R.string.cas_phone_invalid_cmd_tip_message));
                break;
            case CasState.CAS_VERIFY_SUCCESS:
                sendClipboardData();
                break;
            case CasState.CAS_VERIFY_PARAMETER_MISSING:
            case CasState.CAS_VERIFY_PARAMETER_INVALID:
            case CasState.CAS_VERIFY_AESKEY_QUERY_FAILED:
            case CasState.CAS_VERIFY_AESKEY_INVALID:
            case CasState.CAS_VERIFY_DECRYPT_FAILED:
            case CasState.CAS_VERIFY_FAILED:
                showDialog(getResources().getString(R.string.cas_phone_start_auth_fail_tip_message));
                break;
            case CasState.CAS_VERIFY_SESSION_ID_INVALID:
                try {
                    mCloudPhone.exitCloudPhone();
                } catch (Exception e) {
                    CASLog.e(TAG, "Stop cloud phone failed. ", e.getMessage());
                }
                showDialog(getResources().getString(R.string.cas_phone_session_invalid_tip_message));
                break;
            case CasState.CAS_TRAIL_PLAY_TIMEOUT:
                mIsTrailPlayTimeout = true;
                if (!mIsActivityBackground) {
                    CASLog.i(TAG, "avtivity running foreground");
                    showDialog(getResources().getString(R.string.cas_phone_play_timeout_tip_message));
                } else {
                    CASLog.i(TAG, "avtivity running background");
                }
                break;
            case CasState.CAS_BACKGROUND_TIMEOUT:
                stopCloudPhone();
                break;
            case CasState.CAS_SWITCH_FOREGROUND_SUCCESS:
                setStartSuccessFlag();
                break;
            case CasState.CAS_CONNECT_LOST:
                try {
                    mCloudPhone.exitCloudPhone();
                } catch (Exception e) {
                    CASLog.e(TAG, "exitCloudPhone failed");
                }
                showDialog(getResources().getString(R.string.cas_phone_reconnect_server_fail_tip_message));
                break;
            case CasState.CAS_NOTOUCH_TIMEOUT:
                showDialog(getResources().getString(R.string.cas_notouch_timeout_tip_message));
                break;
            case CasState.CAS_OTHERS_CONNECTED:
                try {
                    mCloudPhone.exitCloudPhone();
                } catch (Exception e) {
                    CASLog.e(TAG, "exitCloudPhone failed");
                }
                showDialog(getResources().getString(R.string.cas_others_connected));
                break;
            case CasState.CAS_BACK_HOME:

                if (isReadyToExit) {
                    stopCloudPhone();
                } else {
                    mToast.setText(R.string.cas_exit_phone_confirmation);
                    mToast.show();
                    handler.sendEmptyMessageDelayed(MSG_CANCLE_EXIT, 1000);
                    isReadyToExit = true;
                }
                break;
            default:
                break;
        }
    }

    private void handleStartSuccessMsg(String msg) {
        if (mMediaConfig == null) {
            mMediaConfig = new HashMap<String, String>();
        }

        Gson gson = new Gson();
        HashMap<String, Object> cloudMediaConfig = gson.fromJson(msg, new HashMap<String, Object>().getClass());
        mResolution = String.valueOf(((Double) cloudMediaConfig.get(WIDTH)).intValue());
        mFrameType = (String) cloudMediaConfig.get(FRAME_TYPE);
        CASLog.i(TAG, "mResolution = " + mResolution + ", mFrameType = " + mFrameType);
    }

    /**
     * set full screen display
     */
    private void setFullScreen() {
        CASLog.d(TAG, "@setFullScreen: FEATURE_NO_TITLE & FLAG_FULLSCREEN");

        Window window = getWindow();
        WindowManager.LayoutParams params = window.getAttributes();

        // set full screen
        window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        int vis = window.getDecorView().getSystemUiVisibility();
        window.getDecorView().setSystemUiVisibility(vis
                | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);

        if (Build.VERSION.SDK_INT >= 28) {
            // Adapt by setting parameters
            params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
        } else {
            CasAdaptPhoneUtils.getInstance().adaptPhoneNotch(this, params);
        }

        params.systemUiVisibility = View.INVISIBLE;
        window.setAttributes(params);
    }

    /**
     * main thread set rotation
     */
    public void setRotation(int rotation) {
        if (rotation > 24) {
            return;
        }
        if (currentRotation != rotation) {
            switch (rotation) {
                case 0:
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    break;
                case 1:
                case 8:
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
                    break;
                case 2:
                case 16:
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT);
                    break;
                case 3:
                case 24:
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                    break;
                default:
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    break;
            }
            currentRotation = rotation;
        }
    }

    private void handlePermissionMsg(CloudPhonePermissionInfo permissionInfo) {
        if (permissionInfo != null) {
            ActivityCompat.requestPermissions(this, permissionInfo.getPermissions(), permissionInfo.getRequestCode());
        }
    }

    /**
     * determine whether it is a horizontal screen
     */
    private boolean isLandscape() {
        Configuration mConfiguration = this.getResources().getConfiguration();
        int orientation = mConfiguration.orientation;
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            return true;
        }
        return false;
    }

    private String getTowerInfo() {
        int rssi = -1;
        int subscriberId;
        StringBuilder tower = new StringBuilder("基站数据>>");

        SubscriptionManager subscriptionManager = (SubscriptionManager) this.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{ Manifest.permission.READ_PHONE_STATE }, 101);
            tower.append("\n未授权");
            return tower.toString();
        }
        TelephonyManager tm = (TelephonyManager) this.getSystemService(Context.TELEPHONY_SERVICE);
        List<SubscriptionInfo> subInfos = subscriptionManager.getActiveSubscriptionInfoList();
        if (subInfos.isEmpty()) {
            tower.append("\n无sim卡");
            return tower.toString();
        }
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
            tower.append("\n当前手机版本过低，无法获取数据");
            return tower.toString();
        }
        subscriberId = SubscriptionManager.getActiveDataSubscriptionId();
        for (SubscriptionInfo subInfo:subInfos) {
            if (subscriberId != subInfo.getSubscriptionId()) {
                continue;
            }
            TelephonyManager tm2 = tm.createForSubscriptionId(subInfo.getSubscriptionId());

            tower.append("\nmcc : ").append(subInfo.getMccString());
            tower.append("\nmnc : ").append(subInfo.getMncString());
            String dataType = "";

            List<CellSignalStrength> cellSignalStrengths = tm2.getSignalStrength().getCellSignalStrengths();
            for (CellSignalStrength cellSignalStrength:cellSignalStrengths) {
                if (cellSignalStrength instanceof CellSignalStrengthCdma) {
                    dataType = "CDMA";
                    rssi = ((CellSignalStrengthCdma) cellSignalStrength).getCdmaDbm();
                } else if (cellSignalStrength instanceof CellSignalStrengthGsm) {
                    dataType = "GSM";
                    rssi = ((CellSignalStrengthGsm) cellSignalStrength).getDbm();
                } else if (cellSignalStrength instanceof CellSignalStrengthLte) {
                    dataType = "LTE";
                    rssi = ((CellSignalStrengthLte) cellSignalStrength).getDbm();
                } else if (cellSignalStrength instanceof CellSignalStrengthNr) {
                    dataType = "NR";
                    rssi = ((CellSignalStrengthNr) cellSignalStrength).getDbm();
                } else if (cellSignalStrength instanceof CellSignalStrengthTdscdma) {
                    dataType = "TDSCDMA";
                    rssi = ((CellSignalStrengthTdscdma) cellSignalStrength).getDbm();
                } else if (cellSignalStrength instanceof CellSignalStrengthWcdma) {
                    dataType = "WCDMA";
                    rssi = ((CellSignalStrengthWcdma) cellSignalStrength).getDbm();
                }
            }
            tower.append("\nnetType : ").append(dataType);
            tower.append("\nrssi : ").append(rssi);
            tower.append("\noperator : ").append(subInfo.getDisplayName());
        }
        return tower.toString();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_BACK && bIsStart)) {
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
            return true;
        } else if (keyCode == KeyEvent.KEYCODE_HOME) {
            return true;
        } else {
            return super.onKeyDown(keyCode, event);
        }
    }

    /**
     * stop cloud phone
     */
    private void stopCloudPhone() {
        if (mIsStopCloudPhoneCalled) {
            return;
        }
        CASLog.d(TAG, "stop cloud phone");
        if (mTimer != null) {
            mTimer.cancel();
            mTimer = null;
        }
        handler.removeCallbacksAndMessages(null);
        mIsStopCloudPhoneCalled = true;
        mProgressBar.setVisibility(View.VISIBLE);
        mFrameLayout.setVisibility(View.GONE);
        dismissAllDialogs();
        bIsStart = false;
        try {
            mCloudPhone.exitCloudPhone();
            mCloudPhone.deinit();
        } catch (Exception e) {
            CASLog.e(TAG, "stop cloud phone failed " + e.getMessage());
        }
        if (mCasListener != null) {
            mCasListener.onStateChange(CasState.CAS_EXIT, "Exit");
        }

        while (mCloudPhone.getState() != STATE_DEINIT) {
            sleep(3);
        }
        finish();
    }

    /**
     * Dialog:quit app
     */
    public void showUiLevelDialog() {
        if (quitDialog == null || !quitDialog.isShowing()) {
            final View contentView = View.inflate(this, R.layout.cas_set_ui_level, null);
            contentView.setAlpha(0.7f);

            final CasCommonDialog.Builder builder = new CasCommonDialog.Builder(this);
            CasCommonDialog.DialogClickListener dialogListener = new CasCommonDialog.DialogClickListener();

            builder.setTitle(R.string.cas_phone_quit_app_tip_message);
            builder.setContentView(contentView);
            builder.setPositiveButton(getResources().getString(R.string.cas_phone_cancel_tip_message), dialogListener);
            builder.setNegativeButton(getResources().getString(R.string.cas_phone_quit_app_tip_message), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    CASLog.i(TAG, "showUiLevelDialog stopCloudPhone");
                    stopCloudPhone();
                }
            });

            quitDialog = builder.create();
            quitDialog.setCancelable(true);
            if (!CasCloudPhoneActivity.this.isFinishing()) {
                quitDialog.show();
            }
        }
    }

    public void showDialog(String message) {
        final CasCommonDialog.Builder builder = new CasCommonDialog.Builder(this);
        CasCommonDialog.DialogClickListener dialogListener = new CasCommonDialog.DialogClickListener();
        builder.setTitle(R.string.cas_phone_tip);
        builder.setMessage(message);
        builder.setPositiveButton(R.string.cas_phone_quit_app_tip_message, dialogListener);

        mDialog = builder.create();
        mDialog.setCanceledOnTouchOutside(false);
        mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                stopCloudPhone();
            }
        });
        if (!CasCloudPhoneActivity.this.isFinishing()) {
            mDialog.show();
        }
    }

    /**
     * Dialog:dismiss all
     */
    public void dismissAllDialogs() {
        CASLog.i(TAG, "dismiss all dialogs.");
        if (null != quitDialog && quitDialog.isShowing()) {
            quitDialog.dismiss();
            quitDialog = null;
        }

        if (null != mDialog && mDialog.isShowing()) {
            mDialog.dismiss();
            mDialog = null;
        }
    }

    public void sendClipboardData() {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
                if (clipboardManager.hasPrimaryClip() && clipboardManager.getPrimaryClip().getItemCount() > 0) {
                    CharSequence charSequence = clipboardManager.getPrimaryClip().getItemAt(0).getText();
                    try {
                        mCloudPhone.sendClipboardData(charSequence.toString().getBytes());
                    } catch (Exception e) {
                        CASLog.e(TAG, "failed to send clipboard data " + e.getMessage());
                    }
                }
            }
        }, 300);
    }

    /**
     * 启动成功后处理任务
     */
    private void setStartSuccessFlag() {
        bIsStart = true;
    }

    class CloudPhoneOrientationListener implements CloudPhoneOrientationChangeListener {
        @Override
        public void onOrientationChange(int msg) {
            Message message = new Message();
            message.what = CHANGE_ROTA;
            message.arg1 = msg;
            handler.sendMessage(message);
        }
    }

    class CloudPhoneStateListenerImpl implements CloudPhoneStateListener {
        @Override
        public void onNotify(int code, String msg) {
            CASLog.i(TAG, "code = " + code + " msg = " + msg);
            Message message = new Message();
            message.what = MSG_STATE_CHANGE;
            message.arg1 = code;
            message.obj = msg;
            handler.sendMessage(message);
            if (mCasListener != null) {
                mCasListener.onStateChange(code, msg);
            }
            // debug版本，显示调试toast
            if (BuildConfig.IS_DEBUG) {
                final String toastMessage = msg;
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast toast = Toast.makeText(CasCloudPhoneActivity.this, toastMessage, Toast.LENGTH_SHORT);
                        toast.setGravity(Gravity.CENTER, 0, 0);
                        toast.show();
                    }
                });
            }
        }
    }

    class CloudPhonePermissionRequestListenerImpl implements CloudPhonePermissionRequestListener {
        @Override
        public void onRequestPermissions(CloudPhonePermissionInfo permissionInfo) {
            CASLog.i(TAG, "Permission onRequestPermissions " + permissionInfo.getRequestCode() + permissionInfo.getPermissions());
            Message message = new Message();
            message.what = MSG_PERMISSION_REQUEST;
            message.obj = permissionInfo;
            handler.sendMessage(message);
        }
    }

    class CloudAppDataListenerImpl implements CloudAppDataListener {

        @Override
        public void onRecvCloudAppData(byte[] data) {
        }
    }

    class CloudPhoneClipboardListenerImpl implements CloudPhoneClipboardListener {

        @Override
        public void onClipboardChange(byte[] data) {
            String clipboardText = new String(data);
            ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
            ClipData clipData = ClipData.newPlainText(null, clipboardText);
            clipboardManager.setPrimaryClip(clipData);
        }
    }

    private boolean isInView(MotionEvent event, View view) {
        float x = event.getX();
        float y = event.getY();
        return x >= 0 && x <= view.getWidth() && y >= 0 && y <= view.getHeight();
    }

    private void getPhoneConnectInfo(String phoneId) {
        IConnectInfoModel connectInfoModel;
        if (CasCommonUtils.isDirectMode()) {
            connectInfoModel = new com.huawei.cloudapp.model.direct.ConnectInfoModel();
        } else {
            connectInfoModel = new com.huawei.cloudapp.model.management.ConnectInfoModel();
        }
        ConnectInfoPresenter presenter = new ConnectInfoPresenter(connectInfoModel, this);
        presenter.getConnectInfo(mUserInfo, phoneId, mSelectedRegion);
    }

    private void getUserInfo() {
        if (mCasUserRecord == null) {
            mCasUserRecord = new CasRecord(getSharedPreferences(USER_INFO, MODE_PRIVATE));
        }
        String username = mCasUserRecord.getRecord(USERNAME);
        if (username == null || username.isEmpty()) {
            finishAndReLogin(true);
            return;
        }

        String pass = CasCommonUtils.getEncryptPass();
        if (pass == null || pass.isEmpty()) {
            finishAndReLogin(true);
            return;
        }
        String iv = CasCommonUtils.getEncryptIV();
        if (iv == null || iv.isEmpty()) {
            finishAndReLogin(true);
            return;
        }
        String decryptPass = CasAESKeystoreUtils.decryptData(pass, iv);

        if (mUserInfo == null) {
            mUserInfo = new User(username,
                    mCasUserRecord.getRecord(IAM_USERNAME),
                    mCasUserRecord.getHashMapRecord(TOKEN),
                    mCasUserRecord.getHashMapRecord(TOKEN_EXPIRE_TIME),
                    mCasUserRecord.getHashMapRecord(PROJECT_ID),
                    "");
        }
        IUserModel userModel = new com.huawei.cloudapp.model.direct.UserModel();
        UserPresenter userPresenter = new UserPresenter(this, userModel);
        userPresenter.getUser(mUserInfo, decryptPass, mSelectedRegion);
    }

    private void finishAndReLogin(boolean isPrompt) {
        if (isPrompt) {
            //提示客户需要重新登陆
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    ToastParams params = new ToastParams();
                    params.text = getResources().getString(R.string.user_info_invalid_need_login);
                    params.style = new CustomToastStyle(R.layout.toast_error);
                    Toaster.show(params);
                }
            });
        }
        getSharedPreferences(USER_INFO, MODE_PRIVATE).edit().clear().apply();
        Intent intent = new Intent(this, CasCloudLoginActivity.class);
        startActivity(intent);
        finish();
    }

    @Override
    public void handleData(List<Object> list, int count) {
        if (count == 0) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    showDialog(getResources().getString(R.string.cas_phone_get_auth_param_fail_tip_message));
                }
            });
            return;
        }

        if (((List<?>) list).get(0) instanceof User) {
            List<User> users = (List<User>) (List<?>) list;
            User user = users.get(0);
            mCasUserRecord.setRecord(USERNAME, user.getUsername());
            mCasUserRecord.setRecord(IAM_USERNAME, user.getIamUsername());
            HashMap<String, String> tokens = user.getUserToken();
            mCasUserRecord.setHashMapRecord(TOKEN, tokens);
            HashMap<String, String> tokenExpireTimes = user.getUserTokenExpireTime();
            mCasUserRecord.setHashMapRecord(TOKEN_EXPIRE_TIME, tokenExpireTimes);
            HashMap<String, String> projectIds = user.getUserProjectId();
            mCasUserRecord.setHashMapRecord(PROJECT_ID, projectIds);
            mCasUserRecord.setRecord(SESSION_ID, user.getUserSessionId());
            mUserInfo = user;
            getPhoneConnectInfo(mPhoneId);
        } else{
            List<CasConnectInfo> casConnectInfoList = (List<CasConnectInfo>) (List<?>) list;
            mConnectInfo = casConnectInfoList.get(0);
            HashMap<String, String> parasMap = new HashMap<String, String>();
            parasMap.put(IP, mConnectInfo.getConnectIp());
            parasMap.put(PORT, mConnectInfo.getConnectPort());
            parasMap.put(SESSION_ID, mConnectInfo.getSessionId());
            parasMap.put(TICKET, mConnectInfo.getTicket());
            parasMap.put(AES_KEY, mConnectInfo.getAesKey());
            parasMap.put(AUTH_TS, mConnectInfo.getAuthTs());
            parasMap.put(BACKGROUND_TIMEOUT, mConnectInfo.getBackgroundTimeout());
            parasMap.put(AVAILABLE_PLAYTIME, mConnectInfo.getAvailablePlayTime());
            parasMap.put(USER_ID, mConnectInfo.getUserId());
            parasMap.put(TOUCH_TIMEOUT, mConnectInfo.getTouchTimeout());
            parasMap.put(CLIENT_MODE, CasCommonUtils.isDirectMode() ? DIRECT : MANAGEMENT);
            parasMap.put(REGION_ID, mSelectedRegion);
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        mCloudPhone.startCloudPhone(CasCloudPhoneActivity.this, mFrameLayout, parasMap);
                    } catch (IllegalArgumentException e) {
                        showDialog(getResources().getString(R.string.cas_phone_input_param_invalid));
                    } catch (Exception e) {
                        showDialog(getResources().getString(R.string.cas_phone_start_fail_tip_message));
                    }
                }
            });
        }

    }

    @Override
    public void handleError(Exception e) {
        Log.e(TAG, "handleError: " + e);
        if (e instanceof CustomException.TokenExpireException) {
            getUserInfo();
            return;
        }
        String message;
        if (e instanceof CustomException.LogInInfoInvalidException) {
            getSharedPreferences(USER_INFO, MODE_PRIVATE).edit().clear().apply();
            message = getResources().getString(R.string.user_info_invalid_need_login);
        } else if (
                e instanceof CustomException.FailedToGetConnectInfoException) {
            message = getResources().getString(R.string.cas_phone_get_auth_param_fail_tip_message);
        } else if (e instanceof CustomException.EmptyResponseFromServerException) {
            message = getResources().getString(R.string.response_is_empty);
        }  else if (e instanceof CustomException.ResponseParamsException||
                e instanceof CustomException.ConnectInfoErrorException) {
            message = getResources().getString(R.string.connect_info_error);
        } else if (e instanceof CustomException.NoPermissionException) {
            message = getResources().getString(R.string.no_permission);
        }else if (e instanceof CustomException.OpRestrictedException) {
            message = getResources().getString(R.string.op_restricted);
        } else if (e instanceof CustomException.OpSuspendedException) {
            message = getResources().getString(R.string.op_suspended);
        } else if (e instanceof CustomException.OpUnverifiedException) {
            message = getResources().getString(R.string.op_unverified);
        } else if (e instanceof CustomException.ServerError) {
            message = getResources().getString(R.string.service_internal_error);
        } else if (e instanceof CustomException.ParamInvalidException ||
                e instanceof CustomException.ResourceNotFoundException) {
            message = getResources().getString(R.string.param_invalid);
        } else {
            message = getResources().getString(R.string.request_failed);
        }

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                showDialog(message);
            }
        });
    }
}